/*!
 * DOMEffects 0.2.6, Copyright (c) 2008 Pelle Wessman, <http://code.google.com/p/domeffects>, MIT Style License.
 */
/*extern DOMAssistant */
/**
 * @fileOverview Basic support for effects, requires DOMAssistant and DOMAssistantCSS
 * @name DOMEffects Core
 */

var DOMEffects = function() {
	var constVisibility = 'visibility:', constOpacity = 'opacity', constFunc = 'getOpacityRule';
	/** @scope DOMEffects */
	return {
		/**
		 * Creates a CSS-rule for opacity
		 * @param  {int} value The value of opacity - between 0 and 1 
		 * @return {string} The rule for the opacity
		 * @example //Returns 'visibility: visible; opacity: 1;
		 * // filter: progid:DXImageTransform.Microsoft.Alpha(opacity=100);' in Internet Explorer
		 * //Returns 'visibility: visible; opacity: 1;' in other browsers
		 * DOMEffects.getOpacityRule(1);
		 * @example //Returns 'visibility: hidden; opacity: 0;
		 * // filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0);' in Internet Explorer
		 * //Returns 'visibility: hidden; opacity: 0;' in other browsers
		 * DOMEffects.getOpacityRule(0);
		 */
		getOpacityRule: function(value){
			this[constFunc] = window.ActiveXObject ? function(value){
					return constOpacity + ':' + value + ';' + 'filter:progid:DXImageTransform.Microsoft.Alpha(' + constOpacity + '=' + (value * 100) + ');';
				} : function(value){
					return (value !== 0 ? constVisibility + 'visible;' : constVisibility + 'hidden;') + constOpacity + ':' + value + ';';
				};
			return this[constFunc](value);
		}
	};
}();

DOMAssistant.attach(function() {
	var constQueue = 'q', constFrom = 'f', constTimer = 't', constDuration = 'duration', constStart = 's', constAttr = 'a', constEasing = 'easing',
	    constCallback = 'c', constValue = 'value', constUnit = 'unit', constColor = 'color', constSetTimeout = setTimeout, constFalse = false;
	/**
	 * The default easing: Linear
	 * @private
	 * @return (integer) The new value
	 * @memberOf DOMEffectsAnimation
	 */
	function easeLinear(timediff, base, change, duration) {
		return change*timediff/duration+base;
	}
	
	/**
	 * Gets the current time
	 * @private
	 * @return (integer) The time in milliseconds
	 * @memberOf DOMEffectsAnimation
	 */
	function getTime() {
		return new Date().getTime();
	}
	/**
	 * Parses a CSS-color
	 * @private
	 * @param  {string} value The css-value for the color as either #FDF, #FFDDFF or rgb(255, 221, 255)
	 * @return (object)       The red, green and blue colors as an object with the keys r, g and b
	 */
	function parseColor(value){
		var regexpColor = /#([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})|rgba?\((\d+)\D+(\d+)\D+(\d+)\D*\d*\)/i;
		parseColor = function(value) {
			if (value = regexpColor.exec(value)) {
				return {
					r: value[1] ? parseInt(value[1] + (value[1].length === 1 ? value[1] : ''), 16) : parseInt(value[4], 10),
					g: value[1] ? parseInt(value[2] + (value[2].length === 1 ? value[2] : ''), 16) : parseInt(value[5], 10),
					b: value[1] ? parseInt(value[3] + (value[3].length === 1 ? value[3] : ''), 16) : parseInt(value[6], 10)
				};
			}
		};
		return parseColor(value);
	}
	/**
	 * Initializes an effect
	 * @private
	 * @memberOf DOMEffectsAnimation
	 */
	function init(elem, time) {
		elem.fx[constStart] = time;
		elem.fx[constFrom]  = [];
		var value, attr, obj, answer = [], isObj;
		for (attr in elem.fx[constQueue][0][constAttr]) {
			value = elem.fx[constQueue][0][constAttr][attr];
			isObj = typeof value === 'object';
			if (isObj && value[constUnit] === 'color') {
				if (typeof value[constValue] === 'string') {
					value[constValue] = parseColor(value[constValue]);
				}
				fromValue = parseColor(elem.getStyle(attr));
				elem.fx[constFrom][elem.fx[constFrom].length] = fromValue ? fromValue : {r: 0, g: 0, b: 0};
			} else {
				if (!isObj) {
					obj = {};
					obj[constValue] = value;
					obj[constUnit]  = 'px';
					value = obj;
				} else {
					value[constUnit] = value[constUnit] !== undefined ? value[constUnit] : 'px';
				}
				fromValue = parseFloat(elem.getStyle(attr));
				elem.fx[constFrom][elem.fx[constFrom].length] = isNaN(fromValue) ? 0 : fromValue;
			}

			value[constAttr] = attr;
			answer[answer.length] = value;
		}
		elem.fx[constQueue][0][constAttr] = answer;
	}
	/** @scope DOMAssistant */
	return {
		/**
		 * Stops the animations on the element and clears the queue
		 * @return {object} Returns the element to enable chaining
		 * @example $('div').stop() //Stops all animations of the element and erases all animations from it
		 */
		stop: function() {
			var elem = this;
			if (elem.fx && elem.fx[constTimer]) {
				clearTimeout(elem.fx[constTimer]);
				elem.fx = constFalse;
			}
			return elem;
		},
				
		/**
		 * Starts an animation or if an animation is already in the making adds to the queue
		 * @param  {object}   attributes   An object with the attributes to animate as properties with target values
		 * @param  {object}   [options]    The settings for the animation
		 * @config {integer}  [duration]   Amount of time in milliseconds that the element will be animated
		 * @config {function} [easing]      A function describing an easing equation
		 * @config {function} [callback]   A function to call once the animation is finished
		 * @return {object}                Returns the element to enable chaining
		 * @example $('div').animate({width: 100}); //Animates the element from it's current width to 100 px
		 * @example //Animates the element's opacity to 0 (and hides it because of setStyle)
		 * $('div').animate({opacity: {value: 0, unit: ''}});
		 * @example //Animates the element's background color to red
		 * $('div').animate({'background-color': {value: '#FF0000', unit: 'color'}});
		 * @example //Easing function
		 * function easeExpoOut(timediff, base, change, duration) {
		 * 	return (timediff==duration) ?
		 * 	       base+change :
		 * 	       change * (-Math.pow(2, -10 * timediff/duration) + 1) + base;
		 * }
		 * //Animation with many options set
		 * $('div').animate({width: 100, height: {value: 100, unit: '%'}},
		 * 	{duration: 400, transition: easeExpoOut, callback: function() {
		 * 		console.log('Element animated!');
		 * 	}}
		 * );
		 */
		animate: function(attributes, options) {
			var elem = $$(this), newRow = {};

			options = options ? options : {};

			newRow[constAttr]     = attributes;
			newRow[constCallback] = options.callback;
			newRow[constDuration] = options[constDuration] ? options[constDuration] : 1000;
			newRow[constEasing]   = options[constEasing]   ? options[constEasing]   : easeLinear;

			if (elem.fx) {
				elem.fx[constQueue].push(newRow);
			} else {
				elem.fx = {};
				elem.fx[constQueue] = [newRow];
				init(elem, getTime());

				/**
				 * Moves a step forward in the animation
				 * @private
				 * @memberOf animate
				 */
				var step = function() {
					var timediff = getTime() - elem.fx[constStart],
					current  = elem.fx[constQueue][0],
					duration = current[constDuration],
					style = ';', i = 0, row, value, from;

					if (duration < timediff) {
						timediff = duration;
					}

					for (; i < elem.fx[constQueue][0][constAttr].length; i++) {
						row  = elem.fx[constQueue][0][constAttr][i];
						from = elem.fx[constFrom][i];
						if(row[constUnit] === constColor) {
							style += row[constAttr] + ':rgb(' +
									Math.floor(current[constEasing](timediff, from.r, row[constValue].r - from.r, duration)) + ',' +
									Math.floor(current[constEasing](timediff, from.g, row[constValue].g - from.g, duration)) + ',' +
									Math.floor(current[constEasing](timediff, from.b, row[constValue].b - from.b, duration)) +
								')';
						} else {
							value = current[constEasing](timediff, from, row[constValue] - from, duration);
							style += row[constAttr] === 'opacity' ?
									DOMEffects.getOpacityRule(value) :
									row[constAttr] + ':' + value + row[constUnit] + ';';
						}
					}
					elem.style.cssText += style;

					if (duration === timediff) {
						if (typeof elem.fx[constQueue][0][constCallback] === 'function') {
							elem.fx[constQueue][0][constCallback].call(elem);
						}
						elem.fx[constQueue].shift();
						if (elem.fx[constQueue].length === 0) {
							elem.fx = constFalse;
							return;
						} else {
							init(elem, elem.fx[constStart] + timediff);
						}
					}
					elem.fx[constTimer] = constSetTimeout(step, 30);
				};

				elem.fx[constTimer] = constSetTimeout(step, 30);
			}
			return elem;
		}
	};
}());
/**
 * @fileOverview Highlights an element with a "yellow-fade"-technique, requires DOMEffects
 * @name DOMEffects Highlight
 */

/** @scope DOMAssistant */
DOMAssistant.attach({
	/**
	 * Highlight the background color
	 * @param  {string|object} [options] The options or a color
	 * @config {string}   [color]        The color to flash, defaults to the "yellow-fade" color #ffff99. Should be in an acceptable CSS-format.
	 * @config {string}   [original]     The original background color in one of these three formats: #fff, #ffddff or rgb(255, 255, 255). Defaults to the background-color set in the CSS or if that is transparent to #fff
	 * @config {integer}  [duration]     Amount of time in milliseconds that the element will be animated, default 1.5 second. If 0 the animation will happen immediatly.
	 * @config {boolean}  [nostop]       Should the existing animations stop? Defaults to true.
	 * @config {function} [easing]       A function describing an easing equation, default linear
	 * @config {function} [callback]     A function to be called once the element has been hidden
	 * @config {boolean}  [front]        Should the front-color be animated instead?
	 * @return {object}                  Returns the element to enable chaining
	 * @example $('div').highlight(); //Flashes the element to the "yellow-fade" color and back to it's original color
	 * @example //Easing function
	 * function easeExpoOut(timediff, base, change, duration) {
	 * 	return (timediff==duration) ?
	 * 	       base+change :
	 * 	       change * (-Math.pow(2, -10 * timediff/duration) + 1) + base;
	 * }
	 * //Show-call with many options set
	 * $('div').highlight({color: '#FF0000', duration: 400, transition: easeExpoOut, callback: function() {
	 * 	console.log('Element highlightet!');
	 * }});
	 */
	highlight: function(options){
		var elem = $$(this),
			optionsType = typeof options,
			isObj = optionsType === 'object',
			constColor = 'color',
			constTargetColor = (isObj && options.front) ? constColor : 'background-color',
			attr = {},
			value = optionsType === 'string' ? options : ((isObj && options[constColor]) ? options[constColor] : '#ff9');

		attr[constTargetColor] = {
			value: (isObj && options.original) ? options.original : elem.getStyle(constTargetColor),
			unit: constColor
		};

		if (attr[constTargetColor].value === 'transparent' || attr[constTargetColor].value === 'rgba(0, 0, 0, 0)') {
			attr[constTargetColor].value = '#fff';
		}

		if (isObj && !options.nostop) {
			elem.stop();
		}

		return elem.setStyle(constTargetColor, value).animate(attr, isObj ? options : {} );
	}
});
/**
 * @fileOverview Shows and hides elements, requires DOMEffects
 * @name DOMEffects Show and Hide
 */

/** @scope DOMAssistant */
DOMAssistant.attach({
	/**
	 * Shows an element
	 * @param  {object}   [options]     The options
	 * @config {integer}  [duration]    Amount of time in milliseconds that the element will be animated, default 1.5 second. If 0 the animation will happen immediatly.
	 * @config {function} [easing]      A function describing an easing equation, default linear
	 * @config {function} [callback]    A function to be called once the element has been hidden
	 * @return {object}                 Returns the element to enable chaining
	 * @example $('div').show(); //Shows the element with default options
	 * @example $('div').show({duration: 0}); //Sets the element as shown immediately without animating the process
	 * @example //Easing function
	 * function easeExpoOut(timediff, base, change, duration) {
	 * 	return (timediff==duration) ?
	 * 	       base+change :
	 * 	       change * (-Math.pow(2, -10 * timediff/duration) + 1) + base;
	 * }
	 * //Show-call with many options set
	 * $('div').show({duration: 400, transition: easeExpoOut, callback: function() {
	 * 	console.log('Element shown!');
	 * }});
	 */
	show: function(options){
		options = options ? options : {};
		var elem = $$(this);
		if (options.duration === 0) {
			elem.setStyle(DOMEffects.getOpacityRule(1));
		} else {
			var aniOptions = {};

			aniOptions.duration = options.duration;
			aniOptions.easing   = options.easing;
			aniOptions.callback = options.callback;

			elem.animate({
				opacity: {
					value: 1,
					unit: ''
				}
			}, aniOptions);
		}
		return elem;
	},

	/**
	 * Hides an element
	 * @param  {object}   [options]     The options
	 * @config {boolean}  [remove]      If the element should be removed after it has been hidden, can't be combined with callback
	 * @config {integer}  [duration]    Amount of time in milliseconds that the element will be animated. If 0 the animation will happen immediatly.
	 * @config {function} [easing]      A function describing an easing equation
	 * @config {function} [callback]    A function to be called once the element has been hidden, can't be combined with remove
	 * @return {object}                 Returns the element to enable chaining
	 * @example $('div').hide(); //Hides the element with default options
	 * @example $('div').show({duration: 0}); //Sets the element as hidden immediately without animating the process
	 * @example //Easing function
	 * function easeExpoOut(timediff, base, change, duration) {
	 * 	return (timediff==duration) ?
	 * 	       base+change :
	 * 	       change * (-Math.pow(2, -10 * timediff/duration) + 1) + base;
	 * }
	 * //Hide-call with many options set
	 * $('div').show({remove: true, duration: 400, transition: easeExpoOut);
	 */
	hide: function(options){
		options = options ? options : {};
		var elem = $$(this);
		if (options.duration === 0) {
			elem.setStyle(DOMEffects.getOpacityRule(0));
		} else {
			var aniOptions = {};

			aniOptions.duration = options.duration;
			aniOptions.easing   = options.easing;
			aniOptions.callback = options.remove ? function(){elem.remove();} : options.callback;

			elem.animate({
				opacity: {
					value: 0,
					unit: ''
				}
			}, aniOptions);
		}
		return elem;
	}
});